package com.frog.framework.db.mysql;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;
import org.springframework.transaction.NoTransactionException;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import org.springframework.util.Assert;

import java.util.Objects;

/**
 * 多数据源切面处理
 *
 */
@Aspect
@Component
public class DataSourceAspect implements Ordered {

    private static final Logger log = LoggerFactory.getLogger(DataSourceAspect.class);

    @Pointcut("@annotation(com.frog.framework.db.mysql.UseDataSource)"
            + "|| @within(com.frog.framework.db.mysql.UseDataSource)")
    public void dsPointCut() {}

    @Around("dsPointCut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        UseDataSource useDataSource = getDataSource(point);
        Assert.hasText(useDataSource.value(), "@UseDataSource注解value不能为空");
        Assert.isTrue(DynamicRoutingDataSource.getDataSourceNameSet().contains(useDataSource.value()), "@UseDataSource注解value值不正确,请检查");
        // 切换数据源
        try {
            // 判断 useDataSource与当前数据源是否一致，不一致说明有切换数据源操作
            if (!useDataSource.value().equals(DataSourceContextHolder.getDataBaseType())) {
                try {
                    // 如果存在事务则不允许切换数据源了，直接抛出异常
                    TransactionStatus status = TransactionAspectSupport.currentTransactionStatus();
                    if (status != null) {
                        log.error("当前存在事物,不允许切换数据源");
                        throw new RuntimeException("不允许在事务内切换数据源");
                    }
                } catch (NoTransactionException e) {
                    // 没有管理的事物,继续向下执行
                }
            }
             log.info("当前切换数据源为:" + useDataSource.value());
            DataSourceContextHolder.setDataBaseType(useDataSource.value());
            return point.proceed();
        } finally {
            // 必须清理,否则可能会引起bug
            DataSourceContextHolder.clearDataBaseType();
        }
    }

    /**
     * 获取需要切换的数据源
     */
    public UseDataSource getDataSource(ProceedingJoinPoint point) {
        MethodSignature signature = (MethodSignature) point.getSignature();
        UseDataSource dataSource = AnnotationUtils.findAnnotation(signature.getMethod(), UseDataSource.class);
        if (Objects.nonNull(dataSource)) {
            return dataSource;
        }

        return AnnotationUtils.findAnnotation(signature.getDeclaringType(), UseDataSource.class);
    }

    @Override
    public int getOrder() {
        return HIGHEST_PRECEDENCE;
    }
}
